package org.dreamwork.dsi.embedded.httpd.support;

import org.dreamwork.dsi.embedded.httpd.annotation.AWebParameter;
import org.dreamwork.util.StringUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class WebMappedMethod {
    public static final Pattern PARSER = Pattern.compile ("^(.*?)?\\$\\{(.*?)}(.*?)?$");

    public final Method method;
    public final String pattern;
    public String contentType;

    public List<String> parts;

    public List<WebParameter> parameters;

    public WebMappedMethod (Method method, String pattern) {
        this.method = method;
        this.pattern = pattern;

        if (pattern.contains ("/")) {
            parts = split (pattern);
        }

        if (method == null) {
            throw new NullPointerException ("method");
        }

        if (method.getParameterCount () > 0) {
            Annotation[][] parameterAnnotations = method.getParameterAnnotations ();
            if (method.getParameterCount () != parameterAnnotations.length) {
                throw new IllegalArgumentException ("some parameter of method " + method + " not annotated.");
            }

            parameters = new ArrayList<> (parameterAnnotations.length);
            for (Annotation[] as : parameterAnnotations) {
                AWebParameter awp = null;
                for (Annotation an : as) {
                    if (an instanceof AWebParameter) {
                        awp = (AWebParameter) an;
                        break;
                    }
                }
                if (awp != null) {
                    String name = awp.name ();
                    if (StringUtil.isEmpty (name)) {
                        name = awp.value ();
                    }
                    if (StringUtil.isEmpty (name)) {
                        throw new IllegalArgumentException ("property[name] of AWebParameter is missing!");
                    }

                    WebParameter wp = new WebParameter ();
                    wp.name = name.trim ();
                    if (!StringUtil.isEmpty (awp.defaultValue ())) {
                        wp.defaultValue = awp.defaultValue ().trim ();
                    }
                    wp.type = awp.type ();
                    wp.location = awp.location ();

                    parameters.add (wp);
                } else {
                    throw new IllegalArgumentException ("some parameter of method " + method + " not annotated.");
                }
            }
        }
    }

/*
    @AWebMapping (pattern = "/${projectId}")
    @AWebMapping (pattern = "/${projectId}/update")
    @AWebMapping (pattern = "/something/*
    public Project getProject (String projectId) {
        return service.get (projectId);
    }
*/

    public boolean matches (String pattern, Map<String, String> parsedArgs) {
        if (parts != null && !parts.isEmpty ()) {
            List<String> tmp = split (pattern);
            if (tmp.size () != parts.size ()) {
                return false;
            }
            Map<String, String> map = new HashMap<> ();
            for (int i = 0; i < tmp.size (); i ++) {
                String name = parts.get (i);
                if (name.contains ("${")) {
                    Matcher m = PARSER.matcher (name);
                    if (m.matches ())
                        map.put (m.group (2), tmp.get (i));
                } else if (!name.equals (tmp.get (i))) {    // 任意一部分不匹配，返回false
                    return false;
                }
            }
            if (!map.isEmpty ()) {
                parsedArgs.putAll (map);
            }
            return true;
        } else {
            return pattern.equals (this.pattern);
        }
    }

    public Object invoke (Object instance, Object... args) throws InvocationTargetException {
        try {
            return method.invoke (instance, args);
        } catch (IllegalAccessException ex) {
            throw new RuntimeException (ex);
        }
    }

    private List<String> split (String pattern) {
        String[] tmp = pattern.split ("/");
        List<String> parts = new ArrayList<> (tmp.length);
        for (String p : tmp) {
            if (!StringUtil.isEmpty (p)) {
                parts.add (p.trim ());
            }
        }
        return parts;
    }
}
